package morrior

import (
	"encoding/binary"
	"fmt"
	"iot-base/common/fsm"
	"net"
	"sync"
	"time"
)

type CALLBACK_FUN_DATA func(pPro *MProBody, data []byte, seq uint32)
type CALLBACK_FUN_CMD func(pPro *MProBody, ctype byte, seq uint32)
type CALLBACK_FUN_ERRROR func(seq uint32, err error) bool

type MProBody struct {
	Type byte   //1
	Seq  uint32 //4
	Len  uint32 //4
	Body []byte
}

const BEGIN = 0
const DATA = 1
const SVRCLOSE = 2
const ACCEPT = 4
const CLIENTCLOSE = 5
const END = 6
const ENDTHRD = 7

const BEGIN_MIRROR = 8

const ERROR_ON_CONNECTION = 9

const HB = 10

const ERROR_ON_CONNECTION_SEND = 11

const ERROR_ON_CONNECTION_RECV = 12

type Connection struct {
	conn     net.Conn
	dataFun  CALLBACK_FUN_DATA
	cmdFun   CALLBACK_FUN_CMD
	errFun   CALLBACK_FUN_ERRROR
	sendChan chan MProBody
	pfsm     *fsm.FSMInstance
	seq      uint32
	wg       *sync.WaitGroup
	//timeout       int
	lastAliveTime int64
}

//NewConnection 建立连接
func NewConnection(conn net.Conn, seq uint32, dataFun CALLBACK_FUN_DATA,
	cmdFun CALLBACK_FUN_CMD, errFun CALLBACK_FUN_ERRROR, wg *sync.WaitGroup) *Connection {
	pConn := &Connection{
		conn:          conn,
		sendChan:      make(chan MProBody, 1),
		seq:           seq,
		cmdFun:        cmdFun,
		dataFun:       dataFun,
		errFun:        errFun,
		wg:            wg,
		lastAliveTime: time.Now().Unix(),
		//timeout:  timeout,
	}

	return pConn

}
func (pConn *Connection) SendData(seq uint32, ptype byte, buf []byte) {
	var pro MProBody
	pro.Body = buf
	pro.Type = ptype
	//pro.Type = DATA
	pro.Seq = seq
	pro.Len = uint32(len(buf))
	pConn.sendChan <- pro
}

func sendful(con net.Conn, buf []byte) error {

	var err error = nil
	blen := len(buf)
	lenLeft := blen
	for slen := 0; lenLeft > 0 && err == nil; {
		slen, err = con.Write(buf[(blen - lenLeft):])
		lenLeft -= slen
		//fmt.Println("send--------!!!!!!!!!!!!", slen, err, lenLeft)

	}
	return err
}
func (pConn *Connection) Close() {
	if pConn.wg != nil {
		pConn.wg.Done()
		fmt.Println("wg done---------------------")
	}
	fmt.Println("------------close---------connection--------")
	pConn.conn.Close()
}

func readfull(con net.Conn, buf []byte) (int, error) {
	tlen := len(buf)
	var err error
	rtlen := 0
	rlen := 0
	for err == nil && rtlen < tlen {
		rlen, err = con.Read(buf[rtlen:])
		if rlen == 0 && err != nil {
			nerr, _ := err.(*net.OpError)
			if nerr != nil {
				if nerr.Timeout() || nerr.Temporary() {
					err = nil
					fmt.Println("error----readful----timeout--", nerr)
					continue
				}
			}
			break
			//fmt.Println("error----readful------", err)
			//time.Sleep(0)
			//err = nil
		}
		if rlen > 0 {
			rtlen += rlen
		}

	}
	return rtlen, err

}
func (pConn *Connection) Run(hasKeepAlive bool) {
	pConn.run(hasKeepAlive)
	pConn.Close()
}
func (pConn *Connection) run(hasKeepAlive bool) {
	//buf := make([]byte, 8192)

	go func(pConn *Connection) {
		var buf [9]byte
		for {

			//err :=

			//err := pConn.conn.SetReadDeadline(time.Unix(0, 0))
			//fmt.Println("timeout------", pConn.timeout, err, deadLine)

			//fmt.Println("begin readfull--0000---", time.Now())
			//_, err: = io.ReadFull(pConn.conn, buf[0:9])
			//deadLine := time.Now().Add(time.Duration(pConn.timeout) * time.Second)
			//pConn.conn.SetReadDeadline(deadLine)
			//rLen, err := io.ReadFull(pConn.conn, buf[0:9])
			rLen, err := readfull(pConn.conn, buf[0:9]) //io.ReadFull(pConn.conn, buf[0:9]) //pConn.conn.Read(buf[0:9])
			//fmt.Println("read from kcp-------", rLen, err)

			if err == nil && rLen == 9 {
				pConn.lastAliveTime = time.Now().Unix()
				var pro MProBody
				pro.Type = buf[0]
				pro.Seq = binary.BigEndian.Uint32(buf[1:5])
				pro.Len = binary.BigEndian.Uint32(buf[5:9])
				pro.Body = nil
				if pro.Len > 0 {
					if pro.Len > 100000000 {
						fmt.Println("*************", buf)
					}
					pBuf := make([]byte, pro.Len)
					//pConn.conn.SetReadDeadline(time.Now().Add(time.Duration(pConn.timeout) * time.Hour))
					//pConn.conn.SetReadDeadline(time.Unix(0, 0))
					fmt.Println("begin readfull-----", pro.Len, pro.Seq, pro.Type)
					rlen, err := readfull(pConn.conn, pBuf[:]) //io.ReadFull(pConn.conn, pBuf[:pro.Len])
					if rlen != int(pro.Len) || err != nil {
						fmt.Println("get error------from -ReadFull-----", pro.Len, pro.Seq, rlen, err)
						pConn.errFun(pro.Seq, err)
						break
					}
					pro.Body = pBuf[:]

				} else if int32(pro.Len) < 0 {
					fmt.Println("--------------error----kcp len------", pro.Len)
					break
				} else {
					pro.Body = nil
				}

				if pro.Type == DATA {
					pConn.dataFun(&pro, pro.Body, pro.Seq)
				} else if pro.Type != HB {
					pConn.cmdFun(&pro, pro.Type, pro.Seq)
				}

			} else {
				fmt.Println("---read header----error------------", err, time.Now())
				if pConn.errFun != nil {
					pConn.errFun(pConn.seq, err)
				}
				break

			}
		}
	}(pConn)
	fmt.Println("kcp runing----------==========")
	timer := time.NewTimer(3 * time.Second)
	var buf [9]byte
	var sbuf [21 * 1000]byte
	for {

		select {
		case pro := <-pConn.sendChan:
			if pro.Type == ENDTHRD {
				return
			}

			//fmt.Println("get send task---------------")

			sbuf[0] = pro.Type
			binary.BigEndian.PutUint32(sbuf[1:5], pro.Seq)
			binary.BigEndian.PutUint32(sbuf[5:9], pro.Len)
			copy(sbuf[9:], pro.Body)

			//pConn.seq = pro.Seq
			//fmt.Println("---------sending------", pro.Seq, pro.Len)
			err := sendful(pConn.conn, sbuf[:9+pro.Len])

			if err != nil {
				pConn.errFun(pro.Seq, err)
				fmt.Println("send error------kcp--", err, pro.Seq, pro.Len)
				return
			}
		case <-timer.C:

			tspan := time.Now().Unix() - pConn.lastAliveTime
			if tspan > 20 {
				fmt.Println("timeout------kcp--", tspan)
				return
			}
			timer.Reset(3 * time.Second)
			buf[0] = HB
			binary.BigEndian.PutUint32(buf[1:5], 0)
			binary.BigEndian.PutUint32(buf[5:9], 0)
			err := sendful(pConn.conn, buf[0:9])
			if err != nil {
				fmt.Println("send error------kcp-hb-", err, tspan)
				return
			} else {
				fmt.Println("send hb------kcp-hb-", err, tspan)
			}

		}

		//var pro MProBody

	}

}
